package com.lzyyj.common;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * 代理方式实现的对象包装器
 *
 * @param <T>
 * @author Lzyyj
 */
public class ProxyWrapper<T> implements InvocationHandler {

    private final Object source;

    /**
     * 属性关系映射表
     */
    private Map<String, String> propertyRelation = new HashMap<String, String>();

    /**
     * @param tarClass     目标类
     * @param propertyList 属性映射表，由逗号分隔属性，属性可用冒号指定映射到目标的名称
     * @param source       源对象
     */
    private ProxyWrapper(Class<T> tarClass, String propertyList, Object source) {
        this.source = source;
        if (propertyList == null || propertyList.isEmpty()) {
            // 未指定属性列表，默认为所有属性
            propertyList = BeanUtilsExt.getPropertyList(tarClass);
        }

        // 遍历属性列表，进行属性匹配
        Map<String, Object> sourcePropertyMap = BeanUtilsExt.getPropertyMap(source.getClass());
        Map<String, Object> targetPropertyMap = BeanUtilsExt.getPropertyMap(tarClass);
        String[] properties = propertyList.split(",");

        for (String property : properties) {
            String relationName;
            String[] propertyRelation = property.split(":");
            // 是否有指定映射关系
            switch (propertyRelation.length) {
                case 1:
                    property = propertyRelation[0];
                    relationName = propertyRelation[0];
                    matching(sourcePropertyMap, targetPropertyMap, property, relationName);
                    break;
                case 2:
                    property = propertyRelation[0];
                    relationName = propertyRelation[1];
                    matching(sourcePropertyMap, targetPropertyMap, property, relationName);
                    break;
                default:
                    break;
            }
        }
    }

    public ProxyWrapper(Map<String, String> propertyRelation, Object source) {
        this.propertyRelation = propertyRelation;
        this.source = source;
    }

    /**
     * 对属性进行匹配操作
     *
     * @param sourcePropertyMap 源属性映射表
     * @param targetPropertyMap 目标属性映射表
     * @param property          属性名称
     * @param relationName      关联关系名称
     */
    private void matching(Map<String, Object> sourcePropertyMap, Map<String, Object> targetPropertyMap,
                          String property, String relationName) {
        if (targetPropertyMap.containsKey(property)) {
            // 这是一个属性
            if (sourcePropertyMap.containsKey(relationName) && sourcePropertyMap.get(relationName) != null && sourcePropertyMap.get(relationName).equals(targetPropertyMap.get(property))) {
                propertyRelation.put(property, relationName);
                targetPropertyMap.remove(property);
                sourcePropertyMap.remove(relationName);
            }
        } else {
            // 这不是一个属性，估计是一个方法，先照搬吧
            propertyRelation.put(property, relationName);
        }
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = null;

        Method m = getRelationMethod(method, args);

        if (m != null) {
            result = m.invoke(source, args);
        }

        return result;
    }

    /**
     * 获取关联方法
     *
     * @param method 需要的目标方法名
     * @param args   参数列表
     * @return 找到的关联方法，没有则返回null
     */
    private Method getRelationMethod(Method method, Object[] args) {

        String propertyName = null;
        String ms = null;
        String methodName = method.getName();

        if (methodName.startsWith("is") && args == null && methodName.length() > 2) {
            // is开头，无参，返回类型为 boolea
            propertyName = methodName.substring(2, 3).toLowerCase() + methodName.substring(3);
            ms = "is";
        } else if (methodName.startsWith("get") && args == null && methodName.length() > 3) {
            // 无参，
            propertyName = methodName.substring(3, 4).toLowerCase() + methodName.substring(4);
            ms = "get";
        } else if (methodName.startsWith("set") && args.length == 1 && methodName.length() > 3) {
            propertyName = methodName.substring(3, 4).toLowerCase() + methodName.substring(4);
            ms = "set";
        } else {
            // 非属性封装，应该是一般的方法，先照搬，不做特殊处理
        }

        Method m;
        if (propertyName != null) {
            // 属性操作方法
            if (propertyRelation.containsKey(propertyName)) {
                // 获取映射属性名
                String sourceProperty = propertyRelation.get(propertyName);
                String newMethodName = ms + sourceProperty.substring(0, 1).toUpperCase() + sourceProperty.substring(1);
                Class<?> sourceClass = source.getClass();
                try {
                    m = source.getClass().getMethod(newMethodName, method.getParameterTypes());
                } catch (NoSuchMethodException e) {
                    throw new RuntimeException(e);
                }
            } else {
                try {
                    m = source.getClass().getMethod(methodName, method.getParameterTypes());
                } catch (NoSuchMethodException e) {
                    throw new RuntimeException(e);
                }
            }
        } else {
            // 非属性操作方法
            try {
                m = source.getClass().getMethod(methodName, method.getParameterTypes());
            } catch (NoSuchMethodException e) {
                throw new RuntimeException(e);
            }
        }
        return m;
    }

    public static <T> T wrap(Class<T> clazz, Object source) {
        return wrap(clazz, (String) null, source);
    }

    /**
     * 将源对象包装成指定类型的对象，
     *
     * @param clazz        目标类型
     * @param propertyList 需要包装的属性列表，以逗号分隔，如：id,name,age
     * @param sourceBean   源对象
     * @return 包装后的对象
     */
    public static <T> T wrap(Class<T> clazz, String propertyList, Object sourceBean) {
        T proxyBean = null;

        InvocationHandler invocationHandler = new ProxyWrapper<T>(clazz, propertyList, sourceBean);

        // 获得被代理类所实现的所有接口的数组，在这里数组中只有clazz.class一个元素
        Class<?>[] interfaces = new Class[]{clazz};

        // 获得类加载器
        ClassLoader loader = invocationHandler.getClass().getClassLoader();

        // 获得动态代理类的实例
        proxyBean = (T) java.lang.reflect.Proxy.newProxyInstance(loader, interfaces, invocationHandler);

        return proxyBean;
    }

}